package at.fhooe.mc.msports;


import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.location.Location;

import net.rim.device.api.system.AccelerometerData;
import net.rim.device.api.system.AccelerometerSensor;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.ui.decor.BackgroundFactory;
import net.rim.device.api.util.MathUtilities;
import net.rim.device.api.util.StringProvider;
import at.fhooe.mc.msports.heartrate.IHeartRateListener;
import at.fhooe.mc.msports.heartrate.IHeartRateMonitor;
import at.fhooe.mc.msports.heartrate.SimHRM;
import at.fhooe.mc.msports.ui.ColorableLabel;
import at.fhooe.mc.msports.ui.CustomizedButton;
import at.fhooe.mc.web.XML;

public final class SessionScreen extends MainScreen implements IPedometer,IGps,IHeartRateListener
{
	private static int STATE_IDLE = 0;
	private static int STATE_STARTED = 1;
	private static int STATE_PAUSED = 2;

	private static int NORMAL_MODE = 0;
	private static int HEARTRATE_MODE = 1;
	public static int DEFAULT_STEPS=7000;
	private static int DEFAULT_STEP_SIZE=75;
	
	//Lables
	private ColorableLabel lblHeader;
	private ColorableLabel lblStepCount;
	private ColorableLabel lblDescription;
	private ColorableLabel lblTimerHint;
	private ColorableLabel lblTimer;
	private ColorableLabel lblDistanceHint;
	private ColorableLabel lblDistance;
	private ColorableLabel lblSpeedHint;
	private ColorableLabel lblSpeed;
	private ColorableLabel lblAltitudeHint;
	private ColorableLabel lblAltitude;
	private ColorableLabel lblCaloriesHint;
	private ColorableLabel lblCalories;
	private ColorableLabel lblHeartrateHint;
	private ColorableLabel lblHeartrate;
	
	private PedoManager pedoManager;
	private GPSManager gpsManager;
	private IHeartRateMonitor heartrateManager;
	private int currentMode;
	
	//Buttons
	HorizontalFieldManager controls;
	private CustomizedButton btnStart;
	private CustomizedButton btnResume;
	private String start = "Start";
	private String pause = "Pause";
	private String stop = "Stop";
	private String resume = "Resume";

	private int currentState = 0;
	private Timer timer;
	private int timerCount = 0;
	private int stepCounter=DEFAULT_STEPS;
	
	float altitude=-1;
	private AltitudeCalculation calc = new AltitudeCalculation();
	 
	private Session pedoSession;
	//TODO: integrate other paramters like speed, distance, heartrate
	
    public SessionScreen()
    {        
    	initialize();
    	timer = new Timer();
    	currentMode = NORMAL_MODE;
    }
    
    private void initialize(){
    	
    	
    	VerticalFieldManager mgr = (VerticalFieldManager)getMainManager();
    	mgr.setBackground(BackgroundFactory.createSolidBackground(Color.BLACK));
    	
    	lblHeader = new ColorableLabel("Pedometer", 
    			Color.LIGHTBLUE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,40));
    	lblHeader.setMargin(10,0,0,0);
    	add(lblHeader);
    	
    	//Step count
    	lblStepCount = new ColorableLabel(String.valueOf(DEFAULT_STEPS), 
    			Color.RED, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,65));
    	lblStepCount.setMargin(30,0,0,0);
    	add(lblStepCount);
    	
    	lblDescription = new ColorableLabel("steps to go", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,25));
    	lblDescription.setMargin(5,0,0,0);
    	add(lblDescription);
    	
    	controls = new HorizontalFieldManager(Field.FIELD_HCENTER);
    	
    	VerticalFieldManager left = new VerticalFieldManager(){
    		protected void sublayout(int maxWidth, int maxHeight) {
    			// TODO Auto-generated method stub
    			super.sublayout(Display.getWidth()/2, Display.getHeight()/2);
    		}
    	};
    	
    	VerticalFieldManager right = new VerticalFieldManager(){
    		protected void sublayout(int maxWidth, int maxHeight) {
    			// TODO Auto-generated method stub
    			super.sublayout(Display.getWidth()/2, Display.getHeight()/2);
    		}
    	};
    	
    	//LEFT SIDE
    	//Time elapsed
    	lblTimerHint = new ColorableLabel("Elapsed time", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.BOLD,22));
    	lblTimerHint.setMargin(60,0,0,0);
    	left.add(lblTimerHint);
    	
    	lblTimer = new ColorableLabel("00:00:00", 
    			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,22));
    	lblTimer.setMargin(2,0,0,0);
    	left.add(lblTimer);
    	
    	//Speed
    	lblSpeedHint = new ColorableLabel("Avg. speed", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.BOLD,22));
    	lblSpeedHint.setMargin(10,0,0,0);
    	left.add(lblSpeedHint);
    	
    	lblSpeed = new ColorableLabel("0.0 kph", 
    			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,22));
    	lblSpeed.setMargin(2,0,0,0);
    	left.add(lblSpeed);
    	
    	//Calories
     	lblCaloriesHint = new ColorableLabel("Calories", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.BOLD,22));
     	lblCaloriesHint.setMargin(10,0,0,0);
    	left.add(lblCaloriesHint);
    	
    	lblCalories = new ColorableLabel("0.0 kCal", 
    			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,22));
    	lblCalories.setMargin(2,0,0,0);
    	left.add(lblCalories);
    	
    	//RIGHT SIDE
    	//Distance
    	lblDistanceHint = new ColorableLabel("Distance", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.BOLD,22));
    	lblDistanceHint.setMargin(60,0,0,0);
    	right.add(lblDistanceHint);
    	
    	lblDistance = new ColorableLabel("0.0 km", 
    			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,22));
    	lblDistance.setMargin(2,0,0,0);
    	right.add(lblDistance);

    	//Altitude
    	lblAltitudeHint = new ColorableLabel("Altitude", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.BOLD,22));
    	lblAltitudeHint.setMargin(10,0,0,0);
    	right.add(lblAltitudeHint);
    	
    	if(PersonalData.useGPS()){
    		lblAltitude = new ColorableLabel("0 m", 
    			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,22));
    	}
    	else{
    		lblAltitude = new ColorableLabel("-", 
        			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
        			Font.getDefault().derive(Font.PLAIN,22));		
    	}
    	
    	lblAltitude.setMargin(2,0,0,0);
    	right.add(lblAltitude);
    	
    	//Heartrate
    	lblHeartrateHint = new ColorableLabel("Heartrate", 
    			Color.WHITE, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.BOLD,22));
    	lblHeartrateHint.setMargin(10,0,0,0);
    	right.add(lblHeartrateHint);
    	
    	lblHeartrate = new ColorableLabel("0 bpm", 
    			Color.YELLOW, USE_ALL_WIDTH|DrawStyle.HCENTER, 
    			Font.getDefault().derive(Font.PLAIN,22));
    	lblHeartrate.setMargin(2,0,0,0);
    	right.add(lblHeartrate);
    	
    	//Layout manager
    	HorizontalFieldManager params = new HorizontalFieldManager();
    	params.add(left);
    	params.add(right);
    	add(params);
    	
    	btnStart = new CustomizedButton(USE_ALL_WIDTH|Field.FIELD_HCENTER){
    		protected void layout(int width, int height) {
    			super.layout(getPreferredWidth(), getPreferredHeight());
    			setExtent(getPreferredWidth(), getPreferredHeight());
    		};
    		
    		protected boolean navigationUnclick(int status, int time) {
    			onStateChanged();
    			return true;
    		};
    		
    		protected boolean trackwheelUnclick(int status, int time) {
    			onStateChanged();
    			return true;
    		};
    		
    		public void onStateChanged(){
    			
    			//Start session
    			if(currentState == STATE_IDLE){
    				currentState = STATE_STARTED;
    				startTimer();
    				setLabel(pause);
    		    	pedoSession= new Session(0);
    		    	if(!heartrateManager.isAlive())
    		    		heartrateManager.start();
    		    	else
    		    		startHeartRateListening();
    				invalidate();
    				return;
    			}
    			
    			//Pause session
    			if(currentState == STATE_STARTED){
    				currentState = STATE_PAUSED;
    				pauseTimer();
    				controls.add(btnResume);
    				setWidth(130);
    				setLabel(stop);
    				invalidate();
    				return;
    			}
    			
    			//Stop session
    			if(currentState == STATE_PAUSED){
    				
    				heartrateManager.setRunning(false);
    				if(Dialog.ask(Dialog.D_YES_NO, "Do you want to" +
					" save this session?") == Dialog.YES)
    				{
    					pedoSession.setTimeStamp(System.currentTimeMillis());
    					if(SessionManager.storeSession(pedoSession)){
    						Dialog.inform("Session stored successfully");
    						if(XML.postXML(pedoSession))
    							Dialog.inform("XML transmitted to server.");
    						else
    							Dialog.inform("Something went wronge whil sending the XML");
						}else{
							Dialog.inform("An error occured while storing the session.");
						}
    					
    				}
        			
    				stopTimer();
    				setWidth(280);
    				controls.deleteAll();
    				controls.add(btnStart);
    				currentState = STATE_IDLE;
    				setLabel(start);
    				invalidate();
    				return;
    			}
    		}
    	};
    	
    	btnStart.setWidth(280);
    	btnStart.setHeight(40);
    	btnStart.setLabel(start);
    	btnStart.setFont(Font.getDefault().derive(Font.PLAIN, 20));
    	btnStart.setMargin(20,0,0,0);
    	controls.add(btnStart);
    	
    	btnResume = new CustomizedButton(USE_ALL_WIDTH|Field.FIELD_HCENTER){
    		protected void layout(int width, int height) {
    			super.layout(getPreferredWidth(), getPreferredHeight());
    			setExtent(getPreferredWidth(), getPreferredHeight());
    		};
    		
    		protected boolean navigationUnclick(int status, int time) {
    			onStateChanged();
    			return true;
    		};
    		
    		protected boolean trackwheelUnclick(int status, int time) {
    			onStateChanged();
    			return true;
    		};
    		
    		public void onStateChanged(){
    			
    			currentState = STATE_STARTED;
    			startTimer();
    			btnStart.setWidth(280);
    			btnStart.setLabel(pause);
    			controls.deleteAll();
    			controls.add(btnStart);
    			invalidate();
    			return;
    			
    		}
    	};
    	
    	btnResume.setWidth(130);
    	btnResume.setHeight(40);
    	btnResume.setLabel(resume);
    	btnResume.setFont(Font.getDefault().derive(Font.PLAIN, 20));
    	btnResume.setMargin(20,0,0,0);
    	add(controls);
    
    	
        if(AccelerometerSensor.isSupported()){
        		pedoManager= new PedoManager();
                pedoManager.addListener(this);
        }
        
        if(PersonalData.useGPS()){
	        try{
	        gpsManager= new GPSManager();
	        gpsManager.addListener(this);
	        }
	        catch(Exception e){
	        	e.printStackTrace();
	        }
        }
        heartrateManager= new SimHRM();
        heartrateManager.addListener(this);
    }

    public void startHeartRateListening(){
        heartrateManager.setRunning(true);
    }
    
    public void startTimer(){
    	if(pedoManager!=null){
    		pedoManager.register();
    	}
    	timer.schedule(new TimerTask(){
    			public void run() {
    				UiApplication.getUiApplication().invokeLater(new Runnable() {
						
						public void run() {
							// TODO Auto-generated method stub
							timerCount++;
		    				pedoSession.setDuration(timerCount);
		    				int hours, minutes, seconds;
		    				String strHours, strMinutes, strSeconds;
		    				
		    				hours = timerCount/3600;
		    				strHours = String.valueOf(hours);
		    				if(hours < 10)
		    					strHours = "0" + String.valueOf(hours);
		    				
		    				minutes = timerCount/60;
		    				strMinutes = String.valueOf(minutes);
		    				if(minutes < 10)
		    					strMinutes = "0" + String.valueOf(minutes);
		    				
		    				seconds = timerCount%60;
		    				strSeconds = String.valueOf(seconds);
		    				if(seconds < 10)
		    					strSeconds = "0" + String.valueOf(seconds);
		    				
		    				lblTimer.setText(strHours + ":" + strMinutes + ":" + strSeconds);
						}
					});
    			}
    		},0,1000);
    }
    
    public void stopTimer(){
    	if(pedoManager!=null){
    		pedoManager.unregister();
    	}
    	stepCounter=DEFAULT_STEPS;
    	lblStepCount.setText(String.valueOf(stepCounter));
			UiApplication.getUiApplication().invokeLater(new Runnable() {
				
				public void run() {
					timer.cancel();
					timer = new Timer();
					timerCount = 0;
					pedoSession.setSteps(0);
					pedoSession.setDuration(0);
					if(currentMode == NORMAL_MODE){
						lblSpeed.setText("0.0 kph");
						lblDistance.setText("0.0 km");
						if(PersonalData.useGPS())
							lblAltitude.setText("0 m");
						else
							lblAltitude.setText("-");
						lblCalories.setText("0.0 kCal");
						lblHeartrate.setText("0 bpm");
						lblTimer.setText(String.valueOf("00:00:00"));
					}else{
						lblTimer.setText("00:00:00");
		        		lblDistance.setText("0 bpm");
		        		lblSpeed.setText("0 bpm");
		        		lblAltitude.setText("0 / 0 bpm");
		        		lblHeartrate.setText("0");
		        		lblCalories.setText("0 %");
					}

					lblStepCount.setColor(Color.RED);
				}
			});
    	
    }
    
    public void pauseTimer(){
    	if(pedoManager!=null){
    		pedoManager.unregister();
    	}
    	UiApplication.getUiApplication().invokeLater(new Runnable() {
			public void run() {
				timer.cancel();
				timer = new Timer();
			}
		});
    }
    
    public void makeMenu(Menu menu, int instance) {
    	MenuItem mode = new MenuItem(new StringProvider("Heartrate mode"), 1000,1000) {
    		public void run() {
    			if(currentMode == NORMAL_MODE){
    				currentMode = HEARTRATE_MODE;
    			}
    			else{
    				currentMode = NORMAL_MODE;
    			}	
    			
    			onUiUpdate();
    		}
    	 };
    	 menu.add(mode);
    	 
    	 if(currentMode == NORMAL_MODE)
    		 mode.setText(new StringProvider("Switch to heartrate mode"));
    	 else
    		 mode.setText(new StringProvider("Switch to normal mode"));
    	 
    	 MenuItem data = new MenuItem(new StringProvider("Change personal data"), 1000,1000) {
     		public void run() {
     			UiApplication.getUiApplication().pushScreen(new PersonalDataScreen());
     			if(PersonalData.useGPS())
					lblAltitude.setText("0 m");
				else
					lblAltitude.setText("-");
     		}
     	 };
     	 menu.add(data);
     	 
     	MenuItem stat = new MenuItem(new StringProvider("Statistics"), 1000,1001) {
    		public void run() {
    		   UiApplication.getUiApplication().pushScreen(new StatisticsScreen());
    		}
    	 };
    	 
    	 menu.add(stat);
    	 
    	 super.makeMenu(menu, instance);
    }
    
    protected boolean keyChar(char character, int status, int time) {
    	if (character == Characters.ESCAPE) {
    	    return true;
    	}
    	return super.keyChar(character, status, time);
    }

    public void onUiUpdate(){
    	
    	UiApplication.getUiApplication().invokeLater(new Runnable() {
			
			public void run() {
				
				if(currentMode == NORMAL_MODE){
		    		//Time
		    		lblTimerHint.setText("Elapsed time");
		  
		        	//Speed
		        	lblSpeedHint.setText("Avg. speed");
		        	
		        	//Calories
		         	lblCaloriesHint.setText("Calories"); 
		 
		        	//Distance
		        	lblDistanceHint.setText("Distance");

		        	//Altitude
		        	lblAltitudeHint.setText("Altitude");
		        	
		        	//Heartrate
		        	lblHeartrateHint.setText("Heartrate"); 
		        	
		        	if(currentState == STATE_IDLE){
		        		lblTimer.setText("00:00:00");
		        		lblDistance.setText("0.0 km");
		        		lblSpeed.setText("0.0 kph");
		        		if(PersonalData.useGPS())
		        			lblAltitude.setText("0 m");
		        		else
		        			lblAltitude.setText("-");
		        		lblHeartrate.setText("0 bpm");
		        		lblCalories.setText("0.0 kCal");
		        	}else{
		        		lblDistance.setText(String.valueOf(round(pedoSession.getDistance()))+
		        				" km");
		    			lblSpeed.setText(String.valueOf(round(pedoSession.getAvgSpeed()))+
		    					" kph");
		        		lblHeartrate.setText(String.valueOf(pedoSession.getHeartRate()) + 
		        				" bpm");
		        		lblCalories.setText(String.valueOf(round(pedoSession.getKcalories())) 
		        				+ " kCal");
		        		if(!PersonalData.useGPS())
		        			lblAltitude.setText("-");
		        	}
		    	}else{
		    		//Time
		    		lblTimerHint.setText("Elapsed time");
		  
		        	//Speed
		        	lblSpeedHint.setText("Avg. heartrate");
		        	
		        	//Calories
		         	lblCaloriesHint.setText("% of HRmax"); 
		 
		        	//Distance
		        	lblDistanceHint.setText("Cur. heartrate");

		        	//Altitude
		        	lblAltitudeHint.setText("Min / Max");
		        	
		        	//Heartrate
		        	lblHeartrateHint.setText("TRIMP score"); 
		        	
		        	if(currentState == STATE_IDLE){
		        		lblTimer.setText("00:00:00");
		        		lblDistance.setText("0 bpm");
		        		lblSpeed.setText("0 bpm");
		        		lblAltitude.setText("0 / 0 bpm");
		        		lblHeartrate.setText("0");
		        		lblCalories.setText("0 %");
		        	}else{
		        		lblDistance.setText(String.valueOf(pedoSession.getHeartRate()) + " bpm");
		        		lblSpeed.setText(String.valueOf(pedoSession.getAvgHeartRate()) + " bpm");
		        		lblAltitude.setText(String.valueOf(pedoSession.getMinHeartRate()) + " / " +
		        				String.valueOf(pedoSession.getMaxHeartRate()) + " bpm");
		        		lblHeartrate.setText(String.valueOf(round(pedoSession.getTrimp())));
		        		lblCalories.setText(String.valueOf(pedoSession.getPercentageOfHRMax()) + " %");
		        	}
		    	}
			}
		});
    }
    
	public void onStepDetected() {
		if(currentMode == NORMAL_MODE)
		{
			pedoSession.setSteps(pedoSession.getSteps()+1);
			lblStepCount.setText(String.valueOf(DEFAULT_STEPS-pedoSession.getSteps()));
			lblDistance.setText(String.valueOf(round(pedoSession.getDistance()))+" km");
			lblSpeed.setText(String.valueOf(round(pedoSession.getAvgSpeed()))+" kph");
			if(DEFAULT_STEPS - pedoSession.getSteps() <= 6950){
				lblStepCount.setColor(Color.ORANGE);
			}
			
			if(DEFAULT_STEPS - pedoSession.getSteps() <= 6900){
				lblStepCount.setColor(Color.GREEN);
			}
		}else{
				pedoSession.setSteps(pedoSession.getSteps()+1);
				lblStepCount.setText(String.valueOf(DEFAULT_STEPS-pedoSession.getSteps()));
				
				if(DEFAULT_STEPS - pedoSession.getSteps() <= 6950){
					lblStepCount.setColor(Color.ORANGE);
				}
				
				if(DEFAULT_STEPS - pedoSession.getSteps() <= 6900){
					lblStepCount.setColor(Color.GREEN);
				}
			
		}
	}

	public float round(float num){
		return ((int)(num*1000))/1000f;
	}

	public void onLocationUpdate(Location location) {
		if(currentMode == NORMAL_MODE){
			if(altitude<0)
			   {
			    calc.setAltitude(location.getQualifiedCoordinates().getAltitude());
			   }
			   calc.calculateAltitude(altitude);
			UiApplication.getUiApplication().invokeLater(new Runnable() {
				
				public void run() {
					if(altitude>0){
						lblAltitude.setText(String.valueOf(altitude)+" m");
					}
					else{
						lblAltitude.setText("No GPS");
					}
				}
			});
		}		
	}
	public void onHeartRateUpdated(final int heartRate) {
		if(currentState==STATE_STARTED){
		UiApplication.getUiApplication().invokeLater(new Runnable() {
			
			public void run() {
				if(currentMode==NORMAL_MODE){
					lblHeartrate.setText(String.valueOf(heartRate)+" bpm");
				}
				else{
					lblDistance.setText(String.valueOf(heartRate)+" bpm");
				}
					
					
			}
		});
		pedoSession.setHeartRate(heartRate);
		pedoSession.setTrimp((float) (pedoSession.getTrimp() + (1/30.0)*pedoSession.getPercentageOfHRMax()/100.0f*
				MathUtilities.pow(Math.E*0.64, 1.92*pedoSession.getPercentageOfHRMax()/100.0f)));
		
		int genderFactor = PersonalData.isMale()?1:0;
		
		pedoSession.setKCalories((float) (pedoSession.getKcalories() + (-59.3954 + genderFactor*(-36.3781 + 0.271 * PersonalData.getAge() +
				0.394*PersonalData.getWeight() + 0.404*PersonalData.getV02Max() + 0.634*pedoSession.getHeartRate())
				+ (1-genderFactor)*(0.274*PersonalData.getAge() + 0.103*PersonalData.getWeight() +
						0.380*PersonalData.getV02Max() + 0.45*pedoSession.getHeartRate()))/30.0f/4.168f));
		onUiUpdate();
		}
		System.out.println("new heartrate: "+heartRate);
		
	}

	public void onAccData(AccelerometerData accData) {}

}
